﻿using System;
using System.Threading.Tasks;
using System.Windows.Controls;
using Red.Core;
using Red.Core.Containers;
using Red.EngineData.View;

namespace Red.Controls.ViewModels
{
    /// <summary>
    ///     View model for interop-data based tree items
    /// </summary>
    public class TreeItemEngine : RedTreeItem
    {
        #region Data
        
        private DataView _view;
        private DataView _attachedView;

        /// <summary>
        /// DataView filed name that will be used as children source path. Empty means that all children of the DataView 
        /// will be intaken.
        /// </summary>
        private string _childSource = String.Empty;

        /// <summary>
        /// Specify if Item will enumarate children or not
        /// </summary>
        private bool _enumerateChildren = true;

        #endregion Data

        #region C-tor

        public TreeItemEngine( TreeItemEngine parent, string title, string toolTip, DataView view, Canvas image, Action<TreeItemEngine> onSynchronized = null, string childSourcePath = "", bool enumerateChildren = true )
            : base( parent, title, toolTip, image )
        {
            _view = view;
            if ( _view == null )
            {
                throw new NullReferenceException();
            }

            _childSource = childSourcePath;
            _enumerateChildren = enumerateChildren;

            _view.Refresh( async ex =>
            {
                await RebuildAsync();

                if ( onSynchronized != null )
                    onSynchronized( this );

                OnSynchronized();
            } );
        }

        #endregion C-tor

        #region Properties

        /// <summary>
        ///     Wrapped interop data view
        /// </summary>
        public DataView View
        {
            get { return _view; }
        }

        /// <summary>
        ///     Attached view (e.g. for properties)
        /// </summary>
        public DataView AttachedView
        {
            get { return _attachedView; }
            set { _attachedView = value; }
        }

        #endregion Properties

        #region Public Methods

        /// <summary>
        ///     Synchronizes data with Interop and rebuilds
        /// </summary>
        public void FetchAndRebuild()
        {
            _view.Refresh( async ex =>
                {
                    await RebuildAsync();
                    // TODO: Rebuild should be awaited!
                    OnSynchronized();
                } );
        }

        #endregion

        #region Protected Virtual functions

        /// <summary>
        /// Called when Item enumerates own children
        /// </summary>
        protected virtual RedTreeItem CreateTreeItem( DataView view )
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Called after DataView refresh
        /// </summary>
        protected virtual void OnSynchronized()
        {
        }

        #endregion

        #region Protected implementation

        protected override Task<Result<string>> ExecuteAsyncInternal( string funcName, params object[] callArgs )
        {
            var tcs = new TaskCompletionSource<Result<string>>();
            _view.Execute( Interop.MakeCall( funcName, callArgs ), 
                res => tcs.SetResult( Result<string>.Ok( res.Data ) ), 
                ex => tcs.SetResult( Result<string>.Failed( ex.Message ) ) 
                );
            return tcs.Task;
        }

        protected override sealed Task EnumerateChildrenAsync( IRangedCollection<DataItem> children )
        {
            if ( _enumerateChildren == false || _view.Children.Count == 0 || _view.IsCallable || _view.IsNotifier )
            {
                children.Clear();
                return Task.CompletedTask;
            }

            if ( _childSource != String.Empty )
            {
                if ( _view.Children.Contains( _childSource ) )
                {
                    return CreateChildren( children, _childSource );
                }
                else
                {
                    // _childSource is defined but not found in the DataView
                    throw new ArgumentException();
                }
            }
 
            return CreateChildren( _view, children );
        }

        private Task CreateChildren( IRangedCollection<DataItem> children, string childViewPath )
        {
            var tcs = new TaskCompletionSource<bool>();
            DataView childView = _view.GetSubView( childViewPath );

            childView.Refresh( async ex =>
                {
                    await CreateChildren( childView, children );
                    tcs.SetResult( true );
                } );

            return tcs.Task;
        }

        private Task CreateChildren( DataView view, IRangedCollection<DataItem> children )
        {
            if ( view.Children.Count == 0 )
            {
                children.Clear();
                return Task.CompletedTask;
            }

            var tcs = new TaskCompletionSource<bool>();
            bool clearScheduled = true;

            for ( int i = 0; i < view.Children.Count; ++i )
            {
                int capturedIdx = i;
                string childName = view.Children[i];
                DataView childView = view.GetSubView( childName );

                // NOTE: using awaitable Refresh() will be more convenient (no need to TaskCompletionSource and stuff), but much slower
                // due to better-performing scheduler used in the callback-based version
                childView.Refresh( ex =>
                    {
                        if ( clearScheduled )
                        {
                            children.Clear();
                            clearScheduled = false;
                        }

                        RedTreeItem item = CreateTreeItem( childView );
                        if ( item != null )
                        {
                            children.Add( item );
                        }

                        // we run out of children, so we can continue now
                        if ( capturedIdx == view.Children.Count - 1 )
                        {
                            // we run out of children, so we can continue now
                            tcs.SetResult( true );
                        }
                    } );
            }

            return tcs.Task;
        }

        #endregion
    }
}
